#include "pfgen.h"

using namespace physics;

//////////////////////////////////////////////
//				ParticleForceRegistry
//////////////////////////////////////////////

//////////////////////////////////////////////
// add()
//
// adds the passed particle and PFG (particle force generator) to
// the list of force generators
//////////////////////////////////////////////
void ParticleForceRegistry::add(physics::Particle *particle, ParticleForceGenerator *pfg)
{
	ParticleForceRegistration r;
	r.particle = particle;
	r.pfg = pfg;
	forceGenerators.push_back(r);
}

//////////////////////////////////////////////
// remove()
//
// removes any Particle/PFG combination that matches the passed descriptors
//////////////////////////////////////////////

//////////////////////////////////////////////
// updateForces()
//
// Tells all stored Particle/PFG combinations to update
//////////////////////////////////////////////
void ParticleForceRegistry::updateForces(real dt)
{
	// loop through all PFGs telling them to update their particles
	Registry::iterator i = forceGenerators.begin();
	while (i != forceGenerators.end())
	{
		// what updateForce() does, depends on the
		// overloaded function of the child-PFG (recall that
		// the PFG class is an abstract base class)
		i->pfg->updateForce(i->particle, dt);
		i++;
	}
}

//////////////////////////////////////////////
// ParticleGravity
//////////////////////////////////////////////
//////////////////////////////////////////////
// ParticleGravity()
// 
// Creates the gravitational force based on the passed Vector3
//////////////////////////////////////////////
ParticleGravity::ParticleGravity(const Vector3& gravity)
: gravity(gravity)
{
}

//////////////////////////////////////////////
// updateForce()
//
// Applies the gravitational force to all Particles
//////////////////////////////////////////////
void ParticleGravity::updateForce(physics::Particle* particle, real dt)
{
	// if we have an infinite mass, don't do anything
	if (!particle->hasFiniteMass()) return;

	// Apply the force to the particle
	// since gravity represents the acceleration, we need to multiply by
	// the object's mass (F=ma)
	particle->applyForce(gravity * particle->getMass());

	// note that regardless of how much time has passed, we always apply the same force
}

//////////////////////////////////////////////
// ParticleDrag
//////////////////////////////////////////////
//////////////////////////////////////////////
// ParticleDrag()
// 
// Creates the drag force based on the two components
// passed. k1 and k2 represent the first and second-order
// coefficients for the drag force (which depends on velocity)
//////////////////////////////////////////////
ParticleDrag::ParticleDrag(real k1, real k2)
: k1(k1), k2(k2)
{
}

//////////////////////////////////////////////
// updateForce()
//
// Applies the drag force to all Particles
//////////////////////////////////////////////
void ParticleDrag::updateForce(physics::Particle* particle, real dt)
{
	// get the current velocity (call it force
	// since we'll apply this Vector as a force later)
	Vector3 force;
	particle->getVelocity(&force);

	// Calculate the total drag coefficient
	real dragCoeff = force.magnitude();
	dragCoeff = k1 * dragCoeff + k2 * dragCoeff * dragCoeff;

	// Calculate the final force and apply it
	// keep the same direction, but multiply it by the
	// total drag
	force.normalize();
	force *= -dragCoeff;
	particle->applyForce(force);
}